﻿#region Copyright & License
//
// Project: Quark Framework
// Project site: http://quark.codeplex.com
// See the copyright statement on the project site for more details.
//
// Licensed to the Apache Software Foundation (ASF) under one or more 
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership. 
// The ASF licenses this file to you under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with 
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
#endregion

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Xml.Serialization;
using Quark.Framework.Threading;

namespace Quark.Profiler.Framework
{
    /// <summary>
    /// 分析测试用例的扩展类
    /// </summary>
    public partial class ProfilingTestCase
    {
        #region Constants

        internal const long DEFAULT_LOOP_TIMES = 1;
        internal const long LOOP_FOREVER_TIMES = int.MinValue;

        #endregion

        #region Members

        private ProfilingTestProject _project;
        private IProfilingContext _profilerContext;
        private object _syncRoot;
        private long _state;
        private AtomicBool _stopPending;
        private Random _random;
        private AutoResetEvent _testLoopWaitHandler;
        private ProfilingRequest _lastRequest;

        private List<IProfilingActivity> _activityInstances;
        private Thread _testCaseThread;
        private IDictionary<string, object> _dictActivityDataCollection;

        #endregion

        #region Constructors

        public ProfilingTestCase(ProfilingTestProject project, IProfilingContext context)
        {
            this._project = project;
            this._profilerContext = context;
            this.State = ProfilingTestState.Ready;
            this._stopPending = new AtomicBool();
            this._random = new Random();
            this._testLoopWaitHandler = new AutoResetEvent(false);
            this.Id = Guid.NewGuid().ToString();
            this.Title = ResProfilerFramework.ProfilingTestCaseDefaultTitle;
            this.Description = string.Empty;
            this.LoopTimes = DEFAULT_LOOP_TIMES;
            this.LoopForever = false;
            this.Intervals = new IntervalSettings();
            this.Intervals.Mode = IntervalMode.None;
            this.Intervals.FixedInterval = 0;
            this.Intervals.MinInterval = 0;
            this.Intervals.MaxInterval = 0;
            this.InitializerClass = string.Empty;
            this.DisposerClass = string.Empty;
            this.Activities = new string[] { };

            this._syncRoot = new object();
            this._activityInstances = new List<IProfilingActivity>();
        }

        #endregion

        #region Properties

        /// <summary>
        /// 获取或设置项目当前状态
        /// </summary>
        [XmlIgnore()]
        private ProfilingTestState State
        {
            get
            {
                return (ProfilingTestState)Interlocked.Read(ref _state);
            }
            set
            {
                Interlocked.Exchange(ref _state, (long)value);
            }
        }

        /// <summary>
        /// 获取已创建的活动实例集合
        /// </summary>
        public IProfilingActivity[] ProfilingActivityInstances
        {
            get
            {
                return this._activityInstances.ToArray();
            }
        }

        /// <summary>
        /// 获取最后一次进入的分析测试请求
        /// </summary>
        public ProfilingRequest LastProfilingRequest
        {
            get { return this._lastRequest; }
            set
            {
                System.Threading.Interlocked.Exchange(ref _lastRequest, value);
            }
        }

        #endregion

        #region Internal Events

        /// <summary>
        /// 当前测试用例开始运行之前触发的事件，用于为<see cref="ProfilingTestProject"/>提供事件挂接
        /// </summary>
        internal event ProfilingTestCaseStartingHandler OnTestCaseStartingInternal;
        /// <summary>
        /// 当前测试用例开始运行之前触发的事件，用于为<see cref="ProfilingTestProject"/>提供事件挂接
        /// </summary>
        internal event ProfilingTestCaseStartedHandler OnTestCaseStartedInternal;
        /// <summary>
        /// 当前测试用例运行完毕之后触发的事件，用于为<see cref="ProfilingTestProject"/>提供事件挂接
        /// </summary>
        internal event ProfilingTestCaseFinishedHandler OnTestCaseFinishedInternal;
        /// <summary>
        /// 当前测试用例被停止运行之前触发的事件，用于为<see cref="ProfilingTestProject"/>提供事件挂接
        /// </summary>
        internal event ProfilingTestCaseStoppingHandler OnTestCaseStoppingInternal;

        #endregion

        #region Events

        /// <summary>
        /// 当前测试用例开始运行之前触发的事件
        /// </summary>
        public event ProfilingTestCaseStartingHandler OnTestCaseStarting;
        /// <summary>
        /// 当前测试用例开始运行之前触发的事件
        /// </summary>
        public event ProfilingTestCaseStartedHandler OnTestCaseStarted;
        /// <summary>
        /// 当前测试用例运行完毕之后触发的事件
        /// </summary>
        public event ProfilingTestCaseFinishedHandler OnTestCaseFinished;
        /// <summary>
        /// 当前测试用例被停止运行之前触发的事件
        /// </summary>
        public event ProfilingTestCaseStoppingHandler OnTestCaseStopping;
        /// <summary>
        /// 每次循环完一次测试触发的事件
        /// </summary>
        public event ProfilingTestCaseLoopedHandler OnTestCaseLooped;

        #endregion

        #region TestActivity Management

        private void CreateAllActivities()
        {
            this.ClearAllActivities();

            if (this.Activities != null)
            {
                foreach (string activityClass in this.Activities)
                {
                    IProfilingActivity activity = (IProfilingActivity)Activator.CreateInstance(
                        Type.GetType(activityClass));
                    IProfilingContext context = this._profilerContext;

                    activity.ContextualizeIntoProfiler(context);

                    this._activityInstances.Add(activity);
                }
            }
        }

        private void ClearAllActivities()
        {
            foreach (IProfilingActivity activity in this._activityInstances)
            {
                try
                {
                    activity.Dispose();
                }
                catch (Exception)
                {
                }
            }

            this._activityInstances.Clear();
        }

        #endregion

        #region TestCase Management

        private IDictionary<string, object> CreateActivityParameterDictionary()
        {
            return new Dictionary<string, object>();
        }

        private void AfterAllActivitiesFinished(bool isStopped, ProfilingTestCaseCounter[] counters)
        {
            this.State = ProfilingTestState.Finished;

            if (this.OnTestCaseFinished != null)
            {
                this.OnTestCaseFinished(this, new ProfilingTestCaseFinishedEventArgs(this._project, this._profilerContext, isStopped, counters));
            }

            if (this.OnTestCaseFinishedInternal != null)
            {
                this.OnTestCaseFinishedInternal(this, new ProfilingTestCaseFinishedEventArgs(this._project, this._profilerContext, isStopped, counters));
            }

            try
            {
                if ((this.DisposerClass != null) && (this.DisposerClass.Length > 0))
                {
                    IProfilingTestCaseDisposer disposer = (IProfilingTestCaseDisposer)Activator.CreateInstance(
                        Type.GetType(this.DisposerClass));
                    disposer.DisposeTestCase(this._project, this, this._profilerContext, this._dictActivityDataCollection);
                }
            }
            catch (Exception ex)
            {
                this._profilerContext.LogException(this,
                    string.Format(ResProfilerFramework.ProfilingTestCaseDisposeEx,
                    this.Id, this.Title, this.InitializerClass, ex.Message), ex);
            }
        }

        private bool PerformTestSequence(IList<IProfilingActivity> activities, IDictionary<string, object> dataCollection, ProfilingRequest requestParams, ProfilingTestCaseCounter counter)
        {
            for (int i = 0; i < this._activityInstances.Count; i++)
            {
                IProfilingActivity activity = this._activityInstances[i];
                try
                {
                    activity.Execute(dataCollection, requestParams, counter[i]);
                }
                catch (Exception exActivity)
                {
                    this._profilerContext.LogException(this,
                        string.Format(ResProfilerFramework.ProfilingTestCasePerformTestSequenceInnerEx,
                        this.Id, this.Title, (i + 1), activity.GetType().FullName, exActivity.Message), exActivity);
                }
            }

            return true;
        }

        private ProfilingRequest AcceptNextProfilingRequest()
        {
            return null;
        }

        private bool DoTestLoop(IDictionary<string, object> dataCollection)
        {
            List<ProfilingTestCaseCounter> counters = new List<ProfilingTestCaseCounter>();
            long loopedTimes = 0;
            long loopWill = (this.LoopForever ? 0 : this.LoopTimes);
            bool stopped = false;

            ProfilingTestCaseCounter counter = null;

            try
            {
                for (; !((this._stopPending.Value)
                        || ((!this.LoopForever) && (loopedTimes >= loopWill)));)
                {
                    try
                    {
                        ProfilingRequest requestParams = null;
                        if (this.DistributedSetting != null)
                            requestParams = this.AcceptNextProfilingRequest();

                        counter = new ProfilingTestCaseCounter(this);
                        counter.BeginTime = DateTime.Now;
                        counter.BeginTicks = Environment.TickCount;

                        counter.Succeed = this.PerformTestSequence(this._activityInstances, dataCollection, requestParams, counter);

                        counter.EndTicks = Environment.TickCount;
                        counter.EndTime = DateTime.Now;
                        counters.Add(counter);

                        loopedTimes++;
                    }
                    catch (Exception exLoop)
                    {
                        this._profilerContext.LogException(this,
                            string.Format(ResProfilerFramework.ProfilingTestCaseDoTestLoopEx,
                            this.Id, this.Title, loopedTimes, exLoop.Message), exLoop);
                    }
                    finally
                    {
                        if (this.Intervals.Mode == IntervalMode.Fixed) Thread.Sleep(this.Intervals.FixedInterval);
                        else if (this.Intervals.Mode == IntervalMode.Random) Thread.Sleep(this._random.Next(this.Intervals.MinInterval, this.Intervals.MaxInterval));

                        if (this.OnTestCaseLooped != null)
                        {
                            this.OnTestCaseLooped(this, new ProfilingTestCaseLoopedEventArgs(
                                this._project, this._profilerContext, this.LoopForever, loopedTimes, loopWill));
                        }
                    }
                }
            }
            catch (Exception ex)
            {
                this._profilerContext.LogException(this,
                    string.Format(ResProfilerFramework.ProfilingTestCaseDoTestLoopEx,
                    this.Id, this.Title, loopedTimes, ex.Message), ex);

                stopped = true;
            }
            finally
            {
                if ((counter != null) && (!counters.Contains(counter)))
                {
                    counter.EndTicks = Environment.TickCount;
                    counter.EndTime = DateTime.Now;
                    counters.Add(counter);
                }

                this.AfterAllActivitiesFinished(stopped, counters.ToArray());
            }

            return true;
        }

        private void RunTest(IDictionary<string, object> dataCollection, ProfilingRequest requestParams)
        {
            try
            {
                this.State = ProfilingTestState.Started;
                if (this.OnTestCaseStarted != null)
                {
                    this.OnTestCaseStarted(this, new ProfilingTestCaseEventArgs(this._project, this._profilerContext));
                }
                if (this.OnTestCaseStartedInternal != null)
                {
                    this.OnTestCaseStartedInternal(this, new ProfilingTestCaseEventArgs(this._project, this._profilerContext));
                }

                this.DoTestLoop(dataCollection);
            }
            catch (Exception exOutter)
            {
                this._profilerContext.LogException(this,
                    string.Format(ResProfilerFramework.ProfilingTestCaseRunThreadedTestOutterEx, this.Id, this.Title, exOutter.Message), exOutter);
            }
            finally
            {
            }
        }

        private void RunThreadedTest(object arguments)
        {
            object[] args = (object[])arguments;

            this.RunTest((IDictionary<string, object>)args[0], (ProfilingRequest)args[1]);
        }

        /// <summary>
        /// 运行测试用例
        /// </summary>
        public void StartTestCase()
        {
            lock (this._syncRoot)
            {
                if ((this.Activities == null) || (this.Activities.Length == 0)) throw new NullReferenceException();
                if ((this.State != ProfilingTestState.Ready) && (this.State != ProfilingTestState.Finished))
                    throw new InvalidOperationException();

                if (this.OnTestCaseStarting != null)
                {
                    this.OnTestCaseStarting(this, new ProfilingTestCaseEventArgs(this._project, this._profilerContext));
                }
                if (this.OnTestCaseStartingInternal != null)
                {
                    this.OnTestCaseStartingInternal(this, new ProfilingTestCaseEventArgs(this._project, this._profilerContext));
                }

                this.State = ProfilingTestState.Starting;
                this._stopPending.Value = false;
                this._dictActivityDataCollection = this.CreateActivityParameterDictionary();
                this.CreateAllActivities();

                try
                {
                    if ((this.InitializerClass != null) && (this.InitializerClass.Length > 0))
                    {
                        IProfilingTestCaseInitializer initializer = (IProfilingTestCaseInitializer)Activator.CreateInstance(
                            Type.GetType(this.InitializerClass));
                        initializer.InitializeTestCase(this._project, this, this._profilerContext, this._dictActivityDataCollection);
                    }
                }
                catch (Exception ex)
                {
                    this._profilerContext.LogException(this,
                        string.Format(ResProfilerFramework.ProfilingTestCaseInitializeEx,
                        this.Id, this.Title, this.InitializerClass, ex.Message), ex);
                }

                this._testCaseThread = new Thread(new ParameterizedThreadStart(RunThreadedTest));
                this._testCaseThread.Start(new object[] { this._dictActivityDataCollection, null });
            }
        }

        /// <summary>
        /// 停止运行测试用例
        /// </summary>
        public void StopTestCase()
        {
            lock (this._syncRoot)
            {
                if (this.State != ProfilingTestState.Started) throw new InvalidOperationException();

                if (this.OnTestCaseStopping != null)
                {
                    this.OnTestCaseStopping(this, new ProfilingTestCaseEventArgs(this._project, this._profilerContext));
                }
                if (this.OnTestCaseStoppingInternal != null)
                {
                    this.OnTestCaseStoppingInternal(this, new ProfilingTestCaseEventArgs(this._project, this._profilerContext));
                }

                this.State = ProfilingTestState.Stopping;
                this._stopPending.Value = true;
            }
        }

        #endregion
    }
}
